package rs.dalao.syc.passport.service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import rs.dalao.syc.common.ex.ServiceException;
import rs.dalao.syc.common.pojo.authentication.CurrentPrincipal;
import rs.dalao.syc.common.pojo.po.UserStatePO;
import rs.dalao.syc.common.response.ServiceCode;
import rs.dalao.syc.passport.dao.cache.IUserCacheRepository;
import rs.dalao.syc.passport.dao.persist.repository.IUserRepository;
import rs.dalao.syc.passport.pojo.entity.Doctor;
import rs.dalao.syc.passport.pojo.entity.User;
import rs.dalao.syc.passport.pojo.param.CodeStatusPO;
import rs.dalao.syc.passport.pojo.param.DoctorRegParam;
import rs.dalao.syc.passport.pojo.param.UserLoginInfoParam;
import rs.dalao.syc.passport.pojo.param.UserRegParam;
import rs.dalao.syc.passport.pojo.vo.LoginInfoVO;
import rs.dalao.syc.passport.pojo.vo.LoginRegVO;
import rs.dalao.syc.passport.pojo.vo.UserLoginResultVO;
import rs.dalao.syc.passport.security.CustomUserDetails;
import rs.dalao.syc.passport.service.IUserService;
import rs.dalao.syc.passport.util.SendSmsUtil;

import java.io.Serializable;
import java.security.Principal;
import java.util.*;

@Slf4j
@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private IUserRepository repository;
    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;
    @Autowired
    private IUserCacheRepository iUserCacheRepository;
    @Value("${syc.jwt.secret-key}")
    private String secretKey;
    @Value("${syc.jwt.duration-in-minute}")
    private Long durationInMinute;

    //获取验证码
    @Override
    public  int getCode(String phone){
        Integer code = SendSmsUtil.sendSMS(phone);
        System.out.println(code);
        CodeStatusPO codeStatusPO = new CodeStatusPO();
        codeStatusPO.setCode(code);
        codeStatusPO.setPhone(phone);
        //将验证码保存到redis中
        if(code!=-1){
            iUserCacheRepository.saveCode(phone,codeStatusPO);
        }
        return code;
    }

    //用户登录
    @Override
    public UserLoginResultVO login(UserLoginInfoParam userLoginInfoParam, String remoteAddr, String userAgent) {
        String phone = userLoginInfoParam.getPhone();
        //用户通过短信验证码的方式登录
        if(phone!=null && phone!="" && !phone.isEmpty()){
            LoginInfoVO infoByPhone = repository.getInfoByPhone(phone);
            if(infoByPhone==null){
                //如果通过号码没有找到对应的数据，直接插入数据，并作验证码登录逻辑
                User user = new User();
                user.setUserName(phone);
                //TODO:密码需要调整
                user.setPassword("666666");
                user.setNickName(phone);
                user.setPhone(phone);
                user.setEnable(1);
                user.setRole(1L);
                user.setAvatar("/img/defaultAvatar.png");
                user.setPassword(encoder.encode(user.getPassword()));
                //注册用户
                repository.userReg(user);
            }

            Integer code = userLoginInfoParam.getCode();


            String key = phone;  // 替换成您要获取的键名
            CodeStatusPO codeState = iUserCacheRepository.getCodeState(phone);
            if(codeState==null){
                String message = "验证码错误，请重新输入！";
                throw  new ServiceException(ServiceCode.ERR_CODE_DATA,message);
            }
            Integer value = codeState.getCode();
            System.out.println("value的值为："+value);
            /*if (value.equals(null)) {
                String message="验证码已过期，请重新获取！";
                throw new ServiceException(ServiceCode.ERR_CODE_EXPIRED,message);
            }*/
            if(!value.equals(code)||code.equals(null)){
                String message = "验证码错误，请重新输入！";
                throw  new ServiceException(ServiceCode.ERR_CODE_DATA,message);
            }


            //登录成功后删除redis中的验证码信息
            boolean b = iUserCacheRepository.deleteCodeState(phone);
            System.out.println(b);

            //如果验证码正确，走以下逻辑--拿到登录的用户的id,userName,role,authorities
            //根据用户的手机号码查到数据库里面用户的信息
            LoginInfoVO regInfoByPhone = repository.getInfoByPhone(phone);
            Long id = regInfoByPhone.getId();
            String userName = regInfoByPhone.getUserName();
            Long role = regInfoByPhone.getRole();
            String name = regInfoByPhone.getUserName();
            //从数据库中用户的信息里面拿到权限，是String类型的集合
            Collection<String> authorities = regInfoByPhone.getPermissions();
            List<JSONObject> authorityList = new ArrayList<>();

            if (authorities != null) {
                for (String s : authorities) {
                    JSONObject authorityObject = new JSONObject();
                    authorityObject.put("authority", s);
                    authorityList.add(authorityObject);
                }
            }

            String authorityListJsonString = JSON.toJSONString(authorityList);

            Date date = new Date(System.currentTimeMillis() + 1L*durationInMinute*60 * 1000);
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", id);
            claims.put("userName", userName);
            claims.put("role",role);
            claims.put("remoteAddr",remoteAddr);
            claims.put("userAgent",userAgent);
            //将信息存入到jwt中，然后返换给客户端
            String jwt = Jwts.builder()
                    .setHeaderParam("alg", "HS256")
                    .setHeaderParam("typ", "JWT")
                    .setClaims(claims)
                    .setExpiration(date)
                    .signWith(SignatureAlgorithm.HS256, secretKey)
                    .compact();

            //登录成功后向redis中存入用户的userStatePO信息
            UserStatePO userStatePO = new UserStatePO();
            userStatePO.setEnable(1);
            userStatePO.setAuthoritiesJsonString(authorityListJsonString);
            iUserCacheRepository.saveUserState(id,userStatePO);

            List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
            for (JSONObject authorityObject : authorityList) {
                String authorityValue = authorityObject.getString("authority");
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authorityValue);
                grantedAuthorities.add(grantedAuthority);
            }

            GrantedAuthority[] grantedAuthorityList = grantedAuthorities.toArray(new GrantedAuthority[0]);

            String[] authorityArray = new String[authorities.size()];
            int i = 0;
            for (GrantedAuthority authority : grantedAuthorityList) {
                authorityArray[i++] = authority.getAuthority();
            }

            UserLoginResultVO loginResult = new UserLoginResultVO();
            loginResult.setId(id);
            loginResult.setUsername(userName);
            loginResult.setAvatar(regInfoByPhone.getAvatar());
            loginResult.setRole(role);
            loginResult.setName(name);
            loginResult.setToken(jwt);
            loginResult.setAuthorities(authorityArray);
            return loginResult;
        }

        Authentication authentication = new UsernamePasswordAuthenticationToken
                (userLoginInfoParam.getUserName(), userLoginInfoParam.getPassword());
        Authentication authenticateResult = authenticationManager.authenticate(authentication);

        //当事人信息
        Object principal = authenticateResult.getPrincipal();
        CustomUserDetails userDetails = (CustomUserDetails) principal;
        Long id = userDetails.getId();
        String userName = userDetails.getUsername();
        Long role=userDetails.getRole();
        String name=userDetails.getName();
        Collection<GrantedAuthority> authorities = userDetails.getAuthorities();
        String authorityListJsonString = JSON.toJSONString(authorities);

        Date date = new Date(System.currentTimeMillis() + 1L*durationInMinute*60 * 1000);
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", id);
        claims.put("userName", userName);
        claims.put("role",role);
        claims.put("remoteAddr",remoteAddr);
        claims.put("userAgent",userAgent);
        //将信息存入到jwt中，然后返换给客户端
        String jwt = Jwts.builder()
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                .setClaims(claims)
                .setExpiration(date)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();

        //登录成功后向redis中存入用户的userStatePO信息
        UserStatePO userStatePO = new UserStatePO();
        userStatePO.setEnable(1);
        userStatePO.setAuthoritiesJsonString(authorityListJsonString);
        iUserCacheRepository.saveUserState(id,userStatePO);

        String[] authorityArray = new String[authorities.size()];
        int i = 0;
        for (GrantedAuthority authority : authorities) {
            authorityArray[i++] = authority.getAuthority();
        }

        UserLoginResultVO loginResult = new UserLoginResultVO();
        loginResult.setId(id);
        loginResult.setUsername(userName);
        loginResult.setAvatar(userDetails.getAvatar());
        loginResult.setRole(role);
        loginResult.setName(name);
        loginResult.setToken(jwt);
        loginResult.setAuthorities(authorityArray);
        return loginResult;
    }

    //退出登录
    @Override
    public void logout(CurrentPrincipal currentPrincipal) {
        Long userId = currentPrincipal.getId();
        boolean deleteResult = iUserCacheRepository.deleteUserState(userId);//清除缓存
        if (!deleteResult) {
            String message = "操作失败，用户数据错误！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, message);
        }
    }

    @Autowired
    PasswordEncoder encoder;

    //用户注册
    @Override
    public int userReg(UserRegParam userRegParam) {
        String phone = userRegParam.getPhone();
        LoginInfoVO infoByPhone = repository.getInfoByPhone(phone);
        /*根据名字查看用户是否存在*/
        LoginRegVO userInfo = repository.getRegInfo(userRegParam.getUserName());
        if(userInfo!=null){
            String message ="用户名已存在";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_UNKNOWN,message);
        }
        /*注册用户*/
        User user = new User();
        user.setEnable(1);
        user.setRole(1L);
        BeanUtils.copyProperties(userRegParam,user);
        user.setAvatar("/img/defaultAvatar.png");
        user.setPassword(encoder.encode(userRegParam.getPassword()));
        return repository.userReg(user);

    }

    @Override
    public int doctorReg(DoctorRegParam doctorRegParam) {
        /*根据名字查看医生姓名是否存在*/
        LoginInfoVO doctorInfo = repository.getInfo(doctorRegParam.getUserName());
        if(doctorInfo!=null){
            String message ="用户名已存在";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_UNKNOWN,message);
        }
        /*注册医生*/
        Doctor doctor = new Doctor();
        BeanUtils.copyProperties(doctorRegParam,doctor);
        doctor.setEnable(1);
        doctor.setRole(3l);
//        doctor.setPhone("18883788888");
        doctor.setAvatar("/img/defaultAvatar.png");

        doctor.setPassword(encoder.encode(doctorRegParam.getPassword()));
        return repository.doctorReg(doctor);
    }
}
